import logging

import pendulum
import requests
from starlette.authentication import requires
from starlette.endpoints import HTTPEndpoint
from starlette.responses import JSONResponse, PlainTextResponse

import config
import utils
from authenticate import AuthorizedUser
from database import Session
from model.ModCompanyOperator import CompanyOperator
from model.ModDepartmentVsBureau import DepartmentVsBureau
from redis_connection import redis_conn as redis
from wx_msg_process import signature_url


class JsApiConfig(HTTPEndpoint):

    def __init__(self, scope):
        super().__init__(scope)
        self._logger = logging.getLogger(type(self).__name__)

    async def post(self, request):
        api_ticket = redis.hget(config.ENT_JS_API_TOKEN, "token")
        nonce_str = utils.create_nonce_str()
        timestamp = pendulum.now().int_timestamp
        post_data = await request.json()

        signature = signature_url(api_ticket, timestamp, nonce_str, post_data["req"])

        return JSONResponse({
            "corpid": config.ENT_CORP_ID,
            "timestamp": timestamp,
            "nonce": nonce_str,
            "signature": signature
        })


class OAuthURL(HTTPEndpoint):

    def __init__(self, scope):
        super().__init__(scope)
        self._logger = logging.getLogger(type(self).__name__)

    async def get(self, request):
        self._logger.debug(request.query_params["redirect"])
        redirect_url = request.query_params["redirect"]
        state = "app_develop"

        return PlainTextResponse(f"https://open.weixin.qq.com/connect/oauth2/authorize?"
                                 f"appid={config.ENT_CORP_ID}&redirect_uri={redirect_url}&"
                                 f"response_type=code&scope=snsapi_base&"
                                 f"state={state}#wechat_redirect")


class OAuthUserID(HTTPEndpoint):

    def __init__(self, scope):
        super().__init__(scope)
        self._logger = logging.getLogger(type(self).__name__)

    async def get(self, request):
        session = Session()
        token = redis.hget(config.ENT_ACCESS_TOKEN, "token")
        user_auth_code = request.query_params["code"]
        try:
            id_req = requests.get("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo",
                                  params={"access_token": token, "code": user_auth_code})
            id_resp = id_req.json()

            if "errcode" in id_resp and id_resp["errcode"] == 0:
                if "UserId" in id_resp:
                    user_id = id_resp["UserId"]
                elif "OpenId" in id_resp:
                    self._logger.debug(f"OpenId is {id_resp['OpenId']}")
                    id_convert = requests.post("https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_userid",
                                               params={"access_token": token},
                                               data={"openid": id_resp["OpenId"]})
                    id_conv_resp = id_convert.json()
                    self._logger.debug(f"Error Message: {id_conv_resp['errmsg']}")
                    if "errcode" in id_conv_resp and id_conv_resp["errcode"] == 0:
                        user_id = id_conv_resp["userid"]
                    else:
                        self._logger.info("用户ID转换失败。")
                        return PlainTextResponse("用户ID转换失败。", status_code=500)
                else:
                    self._logger.info("用户ID未能正确解析。")
                    return PlainTextResponse("用户ID未能正确解析。", status_code=500)
            else:
                self._logger.info(f"未能取回用户ID。{id_resp['errmsg']}")
                return PlainTextResponse("未能取回用户ID。", status_code=500)

            user_req = requests.get("https://qyapi.weixin.qq.com/cgi-bin/user/get",
                                    params={"access_token": token, "userid": user_id})
            user_resp = user_req.json()
            if "errcode" in user_resp and user_resp["errcode"] == 0:
                user_info = user_resp
                # 根据用户的OpenID，在Redis中查询Token
                if redis.exists(user_info["userid"]):
                    token = redis.get(user_info["userid"]).decode()
                else:
                    operator_info = session.query(CompanyOperator).filter(
                        CompanyOperator.openid == user_info["userid"]). \
                        one()
                    department_info = session.query(DepartmentVsBureau).filter(
                        DepartmentVsBureau.departmentid == operator_info.departmentid). \
                        one()
                    auth_user = AuthorizedUser(user_info["userid"], user_info["name"], operator_info.departmentid,
                                               operator_info.audit_permission,
                                               department_info.level, from_enterprise=True)
                    token = auth_user.to_jwt().decode("utf-8")
                    redis.setex(user_info["userid"], config.TOKEN_VALID_DURATION, token)
            else:
                return PlainTextResponse("未能取回用户信息。", status_code=500)
        except requests.ConnectionError:
            self._logger.error(f"无法建立与微信服务器的连接。")
            return PlainTextResponse("无法建立与微信服务器的连接。", status_code=500)
        except ValueError:
            self._logger.error(f"微信返回数据解析失败。")
            return PlainTextResponse("微信返回数据解析失败。", status_code=500)
        finally:
            session.rollback()
        return JSONResponse({
            "user_info": user_info,
            "user_token": token
        })


class UserInfo(HTTPEndpoint):

    def __init__(self, scope):
        super().__init__(scope)
        self._logger = logging.getLogger(type(self).__name__)

    async def get(self, request):
        session = Session()
        token = redis.hget(config.ENT_ACCESS_TOKEN, "token")
        user_id = request.query_params["user"]
        try:
            user_req = requests.get("https://qyapi.weixin.qq.com/cgi-bin/user/get",
                                    params={"access_token": token, "userid": user_id})
            user_resp = user_req.json()

            if "errcode" in user_resp and user_resp["errcode"] == 0:
                # 根据用户的OpenID，在Redis中查询Token
                if redis.exists(user_resp["userid"]):
                    token = redis.get(user_resp["userid"]).decode()
                else:
                    operator_info = session.query(CompanyOperator).filter(
                        CompanyOperator.openid == user_resp["userid"]). \
                        one()
                    department_info = session.query(DepartmentVsBureau).filter(
                        DepartmentVsBureau.departmentid == operator_info.departmentid). \
                        one()
                    auth_user = AuthorizedUser(user_resp["userid"], user_resp["name"], operator_info.departmentid,
                                               operator_info.audit_permission,
                                               department_info.level, from_enterprise=True)
                    token = auth_user.to_jwt().decode("utf-8")
                    redis.setex(user_resp["userid"], config.TOKEN_VALID_DURATION, token)
                return JSONResponse({
                    "user_info": user_resp,
                    "user_token": token
                })
            else:
                return PlainTextResponse("未能取回用户信息。", status_code=500)
        except requests.ConnectionError:
            self._logger.error(f"无法建立与微信服务器的连接。")
            return PlainTextResponse("无法建立与微信服务器的连接。", status_code=500)
        except ValueError:
            self._logger.error(f"微信返回数据解析失败。")
            return PlainTextResponse("微信返回数据解析失败。", status_code=500)
        finally:
            session.rollback()


class RefreshToken(HTTPEndpoint):

    def __init__(self, scope):
        super().__init__(scope)

    @requires(["authenticated"])
    async def get(self, request):
        request.user.refresh()
        return PlainTextResponse(request.user.to_jwt().decode("utf-8"))
